/*
Copyright (C) 1996-1997 GX Media, Inc.
Copyright (C) 2010 Ronie Salgado

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "stdafx.h"
#include "Game.h"
#include "Qoole.h"
#include "QDraw.h"
#include "QHollowPrompt.h"
#include "QMainFrame.h"
#include "QTextureView.h"
#include "QTextureManager.h"
#include "QTreeView.h"
#include "QQuadFrame.h"
#include "QConfSheet.h"
#include "QPropSheet.h"
#include "QCompile.h"
#include "QView.h"

IMPLEMENT_DYNAMIC_CLASS(QMainFrame, wxFrame);

BEGIN_EVENT_TABLE(QMainFrame, wxFrame)
	EVT_MENU(wxID_EXIT, QMainFrame::OnExit)
	EVT_MENU(XRCID("menuAbout"), QMainFrame::OnAbout)

	// File menu
	EVT_MENU(XRCID("menuExportMap"), QMainFrame::OnFileExportMap)
	EVT_UPDATE_UI(XRCID("menuExportMap"), QMainFrame::OnFileExportMapUI)
	EVT_MENU(XRCID("menuCompileMap"), QMainFrame::OnFileCompileMap)
	EVT_UPDATE_UI(XRCID("menuCompileMap"), QMainFrame::OnFileCompileMapUI)
	EVT_MENU(XRCID("menuStopCompile"), QMainFrame::OnFileStopCompile)
	EVT_UPDATE_UI(XRCID("menuStopCompile"), QMainFrame::OnFileStopCompileUI)

	// Edit menu
	EVT_MENU(XRCID("editUndo"), QMainFrame::OnEditUndo)
	EVT_UPDATE_UI(XRCID("editUndo"), QMainFrame::OnEditUndoUI)
	EVT_MENU(XRCID("editRedo"), QMainFrame::OnEditRedo)
	EVT_UPDATE_UI(XRCID("editRedo"), QMainFrame::OnEditRedoUI)
	EVT_MENU(wxID_CUT, QMainFrame::OnEditCut)
	EVT_UPDATE_UI(wxID_CUT, QMainFrame::OnEditCutUI)
	EVT_MENU(wxID_COPY, QMainFrame::OnEditCopy)
	EVT_UPDATE_UI(wxID_COPY, QMainFrame::OnEditCopyUI)
	EVT_UPDATE_UI(wxID_PASTE, QMainFrame::OnEditPasteUI)
	EVT_MENU(wxID_DELETE, QMainFrame::OnEditDelete)
	EVT_UPDATE_UI(wxID_DELETE, QMainFrame::OnEditDeleteUI)
	EVT_MENU(XRCID("editHollowOutward"), QMainFrame::OnEditHollow)
	EVT_UPDATE_UI(XRCID("editHollowOutward"), QMainFrame::OnEditHollowUI)
	EVT_MENU(XRCID("editHollowInward"), QMainFrame::OnEditHollow)
	EVT_UPDATE_UI(XRCID("editHollowInward"), QMainFrame::OnEditHollowUI)
	EVT_MENU(XRCID("editSubtract"), QMainFrame::OnEditSubtract)
	EVT_UPDATE_UI(XRCID("editSubtract"), QMainFrame::OnEditSubtractUI)
	EVT_MENU(XRCID("editIntersect"), QMainFrame::OnEditIntersect)
	EVT_UPDATE_UI(XRCID("editIntersect"), QMainFrame::OnEditIntersectUI)
	EVT_MENU(XRCID("menuPreferences"), QMainFrame::OnEditPreferences)
	EVT_MENU(XRCID("menuTextureManager"), QMainFrame::OnEditTextureManager)

	// Mode menu
	EVT_MENU(XRCID("modeObjectMultiTool"), QMainFrame::OnModeObjectMultiTool)
	EVT_UPDATE_UI(XRCID("modeObjectMultiTool"), QMainFrame::OnModeObjectMultiToolUI)
	EVT_MENU(XRCID("modeObjectSelect"), QMainFrame::OnModeObjectSelect)
	EVT_UPDATE_UI(XRCID("modeObjectSelect"), QMainFrame::OnModeObjectSelectUI)
	EVT_MENU(XRCID("modeObjectMove"), QMainFrame::OnModeObjectMove)
	EVT_UPDATE_UI(XRCID("modeObjectMove"), QMainFrame::OnModeObjectMoveUI)
	EVT_MENU(XRCID("modeObjectRotate"), QMainFrame::OnModeObjectRotate)
	EVT_UPDATE_UI(XRCID("modeObjectRotate"), QMainFrame::OnModeObjectRotateUI)
	EVT_MENU(XRCID("modeObjectScale"), QMainFrame::OnModeObjectScale)
	EVT_UPDATE_UI(XRCID("modeObjectScale"), QMainFrame::OnModeObjectScaleUI)
	EVT_MENU(XRCID("modeFaceMove"), QMainFrame::OnModeFaceMove)
	EVT_UPDATE_UI(XRCID("modeFaceMove"), QMainFrame::OnModeFaceMoveUI)
	EVT_MENU(XRCID("modeEdgeMove"), QMainFrame::OnModeEdgeMove)
	EVT_UPDATE_UI(XRCID("modeEdgeMove"), QMainFrame::OnModeEdgeMoveUI)
	EVT_MENU(XRCID("modeVertexMove"), QMainFrame::OnModeVertexMove)
	EVT_UPDATE_UI(XRCID("modeVertexMove"), QMainFrame::OnModeVertexMoveUI)
	EVT_MENU(XRCID("modePlaneClip"), QMainFrame::OnModePlaneClip)
	EVT_UPDATE_UI(XRCID("modePlaneClip"), QMainFrame::OnModePlaneClipUI)
	EVT_MENU(XRCID("modeMirrorFlip"), QMainFrame::OnModeMirrorFlip)
	EVT_UPDATE_UI(XRCID("modeMirrorFlip"), QMainFrame::OnModeMirrorFlipUI)
	EVT_MENU(XRCID("modeEyeMove"), QMainFrame::OnModeEyeMove)
	EVT_UPDATE_UI(XRCID("modeEyeMove"), QMainFrame::OnModeEyeMoveUI)
	EVT_MENU(XRCID("modeEyeRotate"), QMainFrame::OnModeEyeRotate)
	EVT_UPDATE_UI(XRCID("modeEyeRotate"), QMainFrame::OnModeEyeRotateUI)
	EVT_MENU(XRCID("modeEyeZoom"), QMainFrame::OnModeEyeZoom)
	EVT_UPDATE_UI(XRCID("modeEyeZoom"), QMainFrame::OnModeEyeZoomUI)
	EVT_MENU(XRCID("modeFlyThroughPreview"), QMainFrame::OnModeFlyThroughPreview)
	EVT_UPDATE_UI(XRCID("modeFlyThroughPreview"), QMainFrame::OnModeFlyThroughPreviewUI)

	// Object menu.
	EVT_MENU(XRCID("menuProperties"), QMainFrame::OnObjectProperties)
	EVT_MENU(XRCID("menuGroupObjects"), QMainFrame::OnObjectGroup)
	EVT_UPDATE_UI(XRCID("menuGroupObjects"), QMainFrame::OnObjectGroupUI)
	EVT_MENU(XRCID("menuUngroupObjects"), QMainFrame::OnObjectUngroup)
	EVT_UPDATE_UI(XRCID("menuUngroupObjects"), QMainFrame::OnObjectUngroupUI)
	EVT_MENU(XRCID("menuScopeUp"), QMainFrame::OnObjectScopeUp)
	EVT_UPDATE_UI(XRCID("menuScopeUp"), QMainFrame::OnObjectScopeUpUI)
	EVT_MENU(XRCID("menuScopeDown"), QMainFrame::OnObjectScopeDown)
	EVT_UPDATE_UI(XRCID("menuScopeDown"), QMainFrame::OnObjectScopeDownUI)
	EVT_MENU(XRCID("menuLoadPrefab"), QMainFrame::OnObjectLoadPrefab)
	EVT_UPDATE_UI(XRCID("menuLoadPrefab"), QMainFrame::OnObjectLoadPrefabUI)
	EVT_MENU(XRCID("menuSavePrefab"), QMainFrame::OnObjectSavePrefab)
	EVT_UPDATE_UI(XRCID("menuSavePrefab"), QMainFrame::OnObjectSavePrefabUI)

	// Custom menu entries
	EVT_MENU_RANGE(QID_MENUENTRY_START, QID_MENUENTRY_END, QMainFrame::OnMenuEntry)

	// Docking tabs.
	EVT_AUINOTEBOOK_PAGE_CHANGED(QID_LEFT_NOTEBOOK, QMainFrame::OnLeftNotebook)
	EVT_AUINOTEBOOK_PAGE_CHANGING(QID_LEFT_NOTEBOOK, QMainFrame::OnLeftNotebook)

	// Document views.
	EVT_AUINOTEBOOK_PAGE_CHANGED(QID_DOCUMENTS_VIEW, QMainFrame::OnDocumentPageChanged)
	EVT_AUINOTEBOOK_PAGE_CLOSE(QID_DOCUMENTS_VIEW, QMainFrame::OnDocumentPageClosed)
	
	// Frame events
    EVT_CLOSE(QMainFrame::OnCloseWindow)
END_EVENT_TABLE()

int QMainFrame::promptHollow = 1;
int QMainFrame::hollowThickness = 8;
int QMainFrame::promptGroupName = 1;

QMainFrame::QMainFrame()
{
	// Init.
	desktopDocument = NULL;

	operationMode = OPM_OBJECT_MULTITOOL;

	editScope = NULL;
	selector = NULL;

	docManager = NULL;
	processWindow = NULL;
	textureView = NULL;
	propSheet = NULL;
	pmView = NULL;

	lock2dViews = true;
	lock3dView = false;

	displayEntities = true;


	wndMaximized = 0;

	// Config.
	cfg = new LConfig(wxT("QMainFrame"));

	// Edit: Undo levels.
	undoLevels = 50;
	cfg->RegisterVar(wxT("UndoLevels"), &undoLevels, LVAR_INT);
	opManager.SetUndoBufSize(undoLevels);

	// Edit: Hollow.
	cfg->RegisterVar(wxT("PromptHollow"), &promptHollow, LVAR_INT);
	cfg->RegisterVar(wxT("HollowThickness"), &hollowThickness, LVAR_INT);

	// Grouping.
	cfg->RegisterVar(wxT("PromptGroupName"), &promptGroupName, LVAR_INT);

	// Lock views.
	cfg->RegisterVar(wxT("Lock2dViews"), &lock2dViews, LVAR_INT);
	cfg->RegisterVar(wxT("Lock3dView"), &lock3dView, LVAR_INT);

	cfg->RegisterVar(wxT("DisplayEntities"), &displayEntities, LVAR_INT);
	View::DisplayEntities(displayEntities);

	cfg->RegisterVar(wxT("WindowMaximized"), &wndMaximized, LVAR_INT);
}

QMainFrame::~QMainFrame()
{
	// Deinitialize the frame manager.
	auiManager.UnInit();

	SetMenuBar(NULL);

	undoLevels = opManager.GetUndoBufSize();

	cfg->SaveVars();
	delete cfg;
}

bool QMainFrame::Create(wxDocManager *manager)
{
	wxSize dispSize = wxGetDisplaySize();
	wxSize size = dispSize*3/4;
	bool ret = wxFrame::Create(NULL, QID_MAIN_FRAME, wxT("wxQoole"),
			wxDefaultPosition, size);
	if(!ret)
		return false;

	// Store the document manager.
	docManager = manager;

	// Notify wxAUI which has to use.
	auiManager.SetManagedWindow(this);

	// Create the process window.
	processWindow = new QProcessWindow(this);

	// Load the frame elements from the resource.
	wxMenuBar *menu = wxXmlResource::Get()->LoadMenuBar(wxT("mainMenu"));
	SetMenuBar(menu);

	// Create the toolbars.
	CreateToolbars();

	// Init the pop-up menus.
	InitPopupMenus();

	// Create the status bar.
	CreateStatusBar(3);

	// Show the process window.
	processWindow->Show(true);

	// Create the document view.
	documentViews = new wxAuiNotebook(this, QID_DOCUMENTS_VIEW,
					wxDefaultPosition, wxDefaultSize, wxAUI_NB_SCROLL_BUTTONS |
					wxAUI_NB_TAB_MOVE | wxAUI_NB_CLOSE_ON_ACTIVE_TAB);
	
	// Create the left notebook.
	wxAuiNotebook *leftNotebook = new wxAuiNotebook(this, QID_LEFT_NOTEBOOK,
			wxDefaultPosition, wxSize(160, 600),
			wxAUI_NB_BOTTOM|wxAUI_NB_SCROLL_BUTTONS |
			wxAUI_NB_TAB_MOVE);
	leftNotebook->SetAutoLayout(true);
	leftNotebook->Show(true);

	// Create the texture view.
	textureView = new QTextureView(leftNotebook);
	textureView->Show(true);

	// Create the tree view.
	treeView = new QTreeView(leftNotebook);
	treeView->Show(true);

	leftNotebook->AddPage(treeView, _("Tree View"));
	leftNotebook->AddPage(textureView, _("Textures"));

	auiManager.AddPane(documentViews, wxCENTER);
	auiManager.AddPane(leftNotebook, wxAuiPaneInfo().Left()
				.Layer(1).CloseButton(false).Movable(false).Dockable(false).Resizable(true)
				.Floatable(false).Caption(_("Left Panel")));
	auiManager.AddPane(processWindow, wxAuiPaneInfo().Bottom()
				.Caption(_("Process Output"))
				.CloseButton(false).Movable(false).Dockable(false).Resizable(true)
				.Floatable(false));
	/*auiManager.AddPane(textureView, wxAuiPaneInfo().Left().Layer(1)
				.Caption(_("Process Output"))
				.CloseButton(false).Movable(false).Dockable(true).Resizable(true)
				.Floatable(false));
	auiManager.AddPane(treeView, wxAuiPaneInfo().Left().Layer(1)
				.Caption(_("Process Output"))
				.CloseButton(false).Movable(false).Dockable(true).Resizable(true)
				.Floatable(false));*/

	auiManager.Update();

	UpdateFrameTitle();

	return true;
}

void QMainFrame::CreateToolbars()
{
	// Load the toolbars from the resource.
	wxXmlResource::Get()->LoadToolBar(this, wxT("toolbarStandard"));
	//wxXmlResource::Get()->LoadToolBar(this, wxT("toolbarMode"));
}

// Desktop
QooleDoc *QMainFrame::GetDeskTopDocument(void) const
{
	return desktopDocument;
}

void QMainFrame::SetDeskTopDocument(QooleDoc *document)
{
	if(desktopDocument == document)
		return;

	// Clean up the compile stuff.
	if (desktopDocument != NULL)
	{
		Game *game = desktopDocument->GetGame();
		game->CleanUpCompile(desktopDocument->GetDocName());
	}

	// Change the tree view document.
	GetTreeView()->SetCurrentDocument(document);

	// Change the document for the property window.
	if(propSheet)
	{
		if(document)
			propSheet->RegisterDocument(document);
		else
			propSheet->Close(true);
	}

	// Clean up desktop.
	selector = NULL;
	editScope = NULL;

	SwitchOpMode(OPM_OBJECT_MULTITOOL);
	//UpdateStatusBar();

	opManager.ResetUndoList();

	// Set the document.
	desktopDocument = document;

	// Update the window title.
	UpdateFrameTitle();

	if(document == NULL)
		return;

	// Change to the appropriate game.
	bool ok = Game::Set(document->GetGame(), document->GetPalName(),
							QDraw::textureGamma);
	ASSERT(ok);

	// Change to appropriate entity list
	EntList::Set(document->GetEntList());

	// Initialize texture list and select first item
	GetTextureView()->OnNewContents();

	// Init the desktop for new document.
	editScope = document->GetRootObjectPtr();
	selector = document->GetSelector();
	if(!selector)
	{
		selector = new Selector(*editScope);
		document->SetSelector(selector);
	}

	editFocusPos.NewVector(0.0f, 0.0f, 0.0f);

	// Create the quad views.
	QuadViews();

	// Rebuild popup menus.
	DestroyPopupMenus();
	InitPopupMenus();
	BuildBrushMenu();
	BuildEntityMenus();

	// Show cross hair.
	View::ShowCrossHair(lock2dViews);
}

void QMainFrame::UpdateFrameTitle(void)
{
	wxString title;
	if(desktopDocument == NULL)
	{
		title = wxString::Format(wxT("%s %s"), wxGetApp().GetAppName().c_str(),
				wxT(PACKAGE_VERSION));
	}
	else
	{
		title = wxString::Format(wxT("%s - %s %s%s"),
				desktopDocument->GetTitle().c_str(),
				wxGetApp().GetAppName().c_str(),
				wxT(PACKAGE_VERSION),
				desktopDocument->IsModified() ? wxT("*") : wxT(""));
	}

	SetTitle(title);
}
// Operation modes
OperationMode QMainFrame::GetOpMode(void) const
{
	return operationMode;
}

OperationMode QMainFrame::SwitchOpMode(OperationMode mode)
{
	ASSERT(mode >= OPM_MIN && mode <= OPM_MAX);

	OperationMode ret = operationMode;
	if(mode == OPM_MODIFY_BRUSH)
		operationMode = OPM_FACE_MOVE;
	else
		operationMode = mode;

	if(mode == OPM_FLY_THROUGH)
	{
		// Get the 3d view.
		wxPanel *active = GetActiveChild();
		if(active && active->IsKindOf(CLASSINFO(QQuadFrame)))
		{
			QQuadFrame *quad = static_cast<QQuadFrame*> (active);
			QView *view = quad->GetView(QQuadFrame::VID_TOP_RIGHT);
			if(view && view->GetViewType() == QView::VT_3D)
			{
				// Start fly through in the 3d view.
				view->GetViewFrame()->SetFocus();
				view->OnStartFlyThrough();
			}
		}

		// Done with fly through.
		operationMode  = ret;
	}

	if(desktopDocument && selector)
	{
		wxList &views = desktopDocument->GetViews();
		wxList::iterator it = views.begin();
		for(; it != views.end(); it++)
		{
			wxObject *o = *it;
			if(!o->IsKindOf(CLASSINFO(QView)))
				continue;

			QView *view = static_cast<QView*> (o);
			view->OnOpModeChanged(mode, ret);
		}
	}

	UpdateStatusBar();

	return ret;
}

// View layouts
bool QMainFrame::QuadViews(bool createWnds)
{
	assert(desktopDocument);

	// Check if the document already has a view.
	if(desktopDocument->GetViews().size() > 0)
		return true;

	// Create a quad view frame.
	QQuadFrame *quadFrame = new QQuadFrame(this, desktopDocument);

	// Initialize each one of the views.
	for(int i = 0; i < 4; i++)
	{
		QView *view = quadFrame->GetView((QQuadFrame::ViewID)i);

		view->InitView(i, editScope, selector, editFocusPos);
	}

	return true;
}

bool QMainFrame::IsQViewValid(QView *view)
{
	if(!desktopDocument)
		return false;

	wxList &viewList = desktopDocument->GetViews();
	wxList::iterator it = viewList.begin();
	for(; it != viewList.end(); it++)
	{
		wxObject *o = *it;
		if(!o->IsKindOf(CLASSINFO(QView)))
			continue;

		QView *qv = static_cast<QView*> (o);
		if(qv == view)
			return true;
	}

	return false;
}

// Edit Focus and Locking Views
bool QMainFrame::IsLockedView(QView *view)
{
	if(view->GetViewType() == QView::VT_3D)
		return lock3dView;
	return lock2dViews;
}

void QMainFrame::Set2DViewsLock(bool lock)
{
	lock2dViews = lock;
	View::ShowCrossHair(lock);
}

void QMainFrame::Set3DViewLock(bool lock)
{
	lock3dView = lock;
}

void QMainFrame::Set2DLockedZoom(float newZoomVal, bool updateViews)
{
	if (!lock2dViews)
		return;

	wxPanel *active = GetActiveChild();
	if(!active || !active->IsKindOf(CLASSINFO(QQuadFrame)))
		return;

	Vector3d posVec;
	SphrVector oriVec;
	float zoomVal;

	QQuadFrame *quad = static_cast<QQuadFrame *> (active);
	for(int i = 0; i < 4; i++)
	{
		QView *view = quad->GetView(QQuadFrame::ViewID(i));
		if(!view || view->GetViewType() == QView::VT_3D)
			continue;

		view->GetViewState(posVec, oriVec, zoomVal);
		view->SetViewState(posVec, oriVec, newZoomVal, updateViews);
	}
}

void QMainFrame::SetEditFocusPos(Vector3d &focusVec, bool updateViews)
{
	Vector3d oldFocus(editFocusPos);
	editFocusPos = focusVec;

	if (View::IsCrossHairShown())
		View::SetCrossHairPos(editFocusPos);

	wxPanel *active = GetActiveChild();
	if(!active || !active->IsKindOf(CLASSINFO(QQuadFrame)))
		return;

	Vector3d posVec;
	SphrVector oriVec;
	float zoomVal;

	QQuadFrame *quad = static_cast<QQuadFrame*> (active);
	for(int i = 0; i < 4; i++)
	{
		QView *view = quad->GetView(QQuadFrame::ViewID(i));
		if(!view)
			continue;

		if(view->GetViewType() == QView::VT_3D && lock3dView)
		{
			view->GetViewState(posVec, oriVec, zoomVal);
			posVec.SubVector(oldFocus);
			ASSERT(posVec.GetMag() >= 1.0f);
			posVec.NewVector(0.0f, -posVec.GetMag(), 0.0f);

			Matrix44 trans;
 			trans.SetRotate(oriVec);
			trans.Transform(posVec);

			posVec.AddVector(editFocusPos);
			view->SetViewState(posVec, oriVec, zoomVal, updateViews);
		}
		else if(view->GetViewType() != QView::VT_3D && lock2dViews)
		{
			view->GetViewState(posVec, oriVec, zoomVal);
			view->SetViewState(editFocusPos, oriVec, zoomVal, updateViews);
		}
	}
}

void QMainFrame::GetLockedQViews(std::vector<QView*> &dest)
{
	wxPanel *child = GetActiveChild();
	if(!child->IsKindOf(CLASSINFO(QQuadFrame)))
		return;

	QQuadFrame *quad = static_cast<QQuadFrame*> (child);
	for(int i = 0; i < 4; i++)
	{
		QView *view = quad->GetView(QQuadFrame::ViewID(i));
		if(view->GetViewType() == QView::VT_3D && (lock2dViews || lock3dView))
		{
			dest.push_back(view);
		}
		else if(view->GetViewType() != QView::VT_3D && lock2dViews)
		{
			dest.push_back(view);
		}
	}
}

void QMainFrame::Get3DViews(std::vector<QView*> &dest)
{
	wxPanel *child = GetActiveChild();
	if(!child || !child->IsKindOf(CLASSINFO(QQuadFrame)))
		return;

	QQuadFrame *quad = static_cast<QQuadFrame*> (child);
	for(int i = 0; i < 4; i++)
	{
		QView *view = quad->GetView(QQuadFrame::ViewID(i));
		if(view->GetViewType() == QView::VT_3D)
			dest.push_back(view);
	}
}

void QMainFrame::GetViews(std::vector<QView*> &dest)
{
	wxPanel *child = GetActiveChild();
	if(!child || !child->IsKindOf(CLASSINFO(QQuadFrame)))
		return;

	QQuadFrame *quad = static_cast<QQuadFrame*> (child);
	for(int i = 0; i < 4; i++)
	{
		QView *view = quad->GetView(QQuadFrame::ViewID(i));
		if(view != NULL)
			dest.push_back(view);
	}
}

// Scoping
Object &QMainFrame::GetScope(void)
{
	return *editScope;
}

Selector &QMainFrame::GetSelector()
{
	return *selector;
}

Selector &QMainFrame::ChangeEditScope(Object *newScope)
{
	// Sanity.
	ASSERT(newScope && editScope && selector);

	if(newScope == editScope)
		return *selector;

	ASSERT(newScope);
	ASSERT(desktopDocument->GetRootObjectPtr() == &(newScope->GetRoot()));

	// Need to transform the edit focus pos.
	Matrix44 trans;
	Object::GetTransMatrix(*editScope, *newScope, trans);
	trans.Transform(editFocusPos);

	editScope = newScope;
	delete selector;
	selector = new Selector(*editScope);
	return *selector;
}

QProcessWindow *QMainFrame::GetProcessWindow()
{
	return processWindow;
}

QPropSheet *QMainFrame::GetPropSheet()
{
	return propSheet;
}

QTextureView *QMainFrame::GetTextureView()
{
	return textureView;
}

QTreeView *QMainFrame::GetTreeView()
{
	return treeView;
}

wxAuiNotebook *QMainFrame::GetViewsNotebook()
{
	return documentViews;
}

// Pop-up menus
#define PME_OBJLOAD		1
#define PME_ADDENT		2
#define PME_APPLYENT	3

bool QMainFrame::InitPopupMenus(void)
{
	menuEntryCount = 0;
	for(int i = 0; i < QID_MENUENTRY_END - QID_MENUENTRY_START; i++)
		menuEntry[i] = NULL;

	pmView = new wxMenu;
	pmView->Append(wxID_ANY, _("Add Brush"));
	pmView->AppendSeparator();
	pmView->Append(wxID_ANY, _("Add Entity"));
	pmView->Append(wxID_ANY, _("Apply Entity"));

	return true;
}

void QMainFrame::DestroyPopupMenus(void)
{
	for(int i = 0; i < QID_MENUENTRY_END - QID_MENUENTRY_START; i++)
		DeleteMenuEntry(i);
	menuEntryCount = 0;
	delete pmView;
	pmView = NULL;
}

void QMainFrame::AddMenuEntry(wxMenu *menu, const wxChar *text, int type, const wxChar *data)
{
	ASSERT(menu);
	ASSERT(menuEntryCount < QID_MENUENTRY_END - QID_MENUENTRY_START);

	int id = menuEntryCount++;
	menuEntry[id] = new MenuEntry;
	menuEntry[id]->type = type;
	menuEntry[id]->data = data;

	menu->Append(QID_MENUENTRY_START + id, text);
}

void QMainFrame::DeleteMenuEntry(int nID)
{
	delete menuEntry[nID];
	menuEntry[nID] = NULL;
}

void QMainFrame::OnMenuEntry(wxCommandEvent &event)
{
	ASSERT(event.GetId() >= QID_MENUENTRY_START && event.GetId() <= QID_MENUENTRY_END);

	MenuEntry *entry = menuEntry[event.GetId() - QID_MENUENTRY_START];
	ASSERT(entry);

	if(entry->type == PME_OBJLOAD)
		LoadObject(entry->data);
	else if(entry->type == PME_ADDENT)
		AddEntity(entry->data);
	else if(entry->type == PME_APPLYENT)
		ApplyEntity(entry->data);
}

void QMainFrame::LoadObject(const wxString &name)
{
	wxPanel *frame = GetActiveChild();
	if(!frame)
		return;

	// For now assume that the frame is a QQuadFrame.
	QQuadFrame *quadFrame = static_cast<QQuadFrame*> (frame);
	QView *qview = quadFrame->GetView(QQuadFrame::VID_TOP_LEFT);
	View *view = qview->GetViewPtr();

	Object *obj = new Object;
	if (!obj->LoadObjFile(name)) {
		LError(_("%s not found."), name.c_str());
		delete obj;
		return;
	}

	LinkList<ObjectPtr> objs;
	objs.AppendNode(*(new ObjectPtr(obj)));

	Selector *selector = &GetSelector();

	Vector3d centerVec;
	if (IsLockedView(qview))
	{
		centerVec = GetEditFocusPos();
	}
	else if (qview->GetViewType() == QView::VT_3D)
	{
		centerVec.NewVector(0.0f, 128.0f, 0.0f);
		Matrix44 trans;
		view->CalTransSpaceMatrix(trans.SetIdentity());
		trans.Transform(centerVec);
	}
	else
	{
		centerVec = view->GetPosition();
	}

	qview->SnapAddObjPos(centerVec);

	QooleDoc *doc = qview->GetDocument();
	Texture *texture = GetTextureView()->GetSelTexture();
	if(!texture)
		texture = doc->GetGame()->GetDefaultTexture();
	if(texture)
		doc->TextureApplyObjs(objs, texture->GetName(), false);

	OpObjsAddNew *op = new OpObjsAddNew(objs, centerVec, selector->GetScopePtr());
	CommitOperation(*op);
}

void QMainFrame::AddEntity(const wxString &name)
{
	wxPanel *frame = GetActiveChild();
	if(!frame)
		return;

	// For now assume that the frame is a QQuadFrame.
	QQuadFrame *quadFrame = static_cast<QQuadFrame*> (frame);
	QView *qview = quadFrame->GetView(QQuadFrame::VID_TOP_LEFT);
	View *view = qview->GetViewPtr();

	Object *obj = new Object;
	Entity *ent = new Entity(name);
	obj->SetEntity(ent);

	LinkList<ObjectPtr> objs;
	objs.AppendNode(*(new ObjectPtr(obj)));

	Selector *pSlct = &GetSelector();

	Vector3d centerVec;
	if (IsLockedView(qview))
	{
		centerVec = GetEditFocusPos();
	}
	else if (qview->GetViewType() == QView::VT_3D)
	{
		centerVec.NewVector(0.0f, 128.0f, 0.0f);
		Matrix44 trans;
		view->CalTransSpaceMatrix(trans.SetIdentity());
		trans.Transform(centerVec);
	}
	else
	{
		centerVec = view->GetPosition();
	}

	qview->SnapAddObjPos(centerVec);

	OpObjsAddNew *op = new OpObjsAddNew(objs, centerVec, pSlct->GetScopePtr());
	CommitOperation(*op);
}

void QMainFrame::ApplyEntity(const wxString &name) {
	Selector *pSlctr = &GetMainFrame()->GetSelector();
	if(pSlctr->GetNumMSelectedObjects() == 0)
		return;

	OpEntityApply *op = new OpEntityApply(name);
	CommitOperation(*op);
}

wxMenu *QMainFrame::ReadBrushCategory(wxXmlNode *category, wxMenu *parent)
{
	wxMenu *ret = new wxMenu();

	// Read the category name.
	if(parent != NULL)
	{
		wxString name = category->GetAttribute(wxT("name"), wxT("brushcat"));
		wxString desc = category->GetAttribute(wxT("description"), wxEmptyString);
		parent->Append(wxID_ANY, name, ret, desc);
	}

	// Read the category elements.
	wxXmlNode *catNode = category->GetChildren();
	for(; catNode; catNode = catNode->GetNext())
	{
		if(catNode->GetName() == wxT("brush"))
		{
			// Read the brush data.
			wxString name = catNode->GetAttribute(wxT("name"), wxEmptyString);
			wxString filename = catNode->GetAttribute(wxT("filename"), wxEmptyString);
			if(name.empty() || filename.empty())
				continue;

			AddMenuEntry(ret, name, PME_OBJLOAD, filename);
		}
		else if(catNode->GetName() == wxT("category"))
		{
			// Read the child category.
			ReadBrushCategory(catNode, ret);
		}
	}
	return ret;
}

wxMenu *QMainFrame::CreateBrushMenu(void)
{
	wxFileName setName;
	setName.Assign(Game::Get()->GetPrimitivesSet());
	if(!setName.IsFileReadable())
	{
		fprintf(stderr, "Unexistent brush set file.\n");
		return NULL;
	}

	// Load the config document.
	wxXmlDocument doc;
	if(!doc.Load(setName.GetFullPath()))
	{
		fprintf(stderr, "Failed to load the brush set file.\n");
		return NULL;
	}

	// Check the root element.
	if(doc.GetRoot()->GetName() != wxT("brushset"))
	{
		fprintf(stderr, "Invalid brush set file.\n");
		return NULL;
	}

	// Read the top level category.
	return ReadBrushCategory(doc.GetRoot(), NULL);
}

void QMainFrame::BuildBrushMenu()
{
	QDraw::OutputText(_("Creating build brush menu... "));

	// Add to the top menu bar.
	wxMenuBar *menuBar = GetMenuBar();
	wxMenu *objectMenu = menuBar->GetMenu(3);
	ASSERT(objectMenu);

	int id = XRCID("menuAddBrush");
	wxMenuItem *addBrush = objectMenu->FindItem(id);
	ASSERT(addBrush);

	wxString text = addBrush->GetText();
	objectMenu->Delete(addBrush);
	objectMenu->Insert(0, id, text, CreateBrushMenu());

	// Add to the right-click menu.
	addBrush = pmView->FindItemByPosition(0);
	ASSERT(addBrush);
	pmView->Remove(addBrush);
	pmView->Insert(0, wxID_ANY, text, CreateBrushMenu());

	QDraw::OutputText(_("OK.\n"));
}

void QMainFrame::CreateEntityMenu(wxMenu &addMenu, wxMenu &applyMenu)
{
	ASSERT(desktopDocument);
	EntList *entList = desktopDocument->GetEntList();

	if(!entList)
		return;

	std::vector<wxMenu*> pmAdd, pmApply;
	int cnt;

	IterLinkList<EntClass> iterClass(entList->GetClassList());
	EntClass *entClass;

	cnt = 0;
	iterClass.Reset();
	while(!iterClass.IsDone()) {
		entClass = iterClass.GetNext();
		entClass->count = cnt;

		if(entClass->nonmodels)
			pmAdd.push_back(new wxMenu());
		else
			pmAdd.push_back(NULL);

		if(entClass->models && entClass->name != wxT("Null"))
			pmApply.push_back(new wxMenu());
		else
			pmApply.push_back(NULL);
		cnt++;
	}

	//printf("Entclass count: %d\n", cnt);
	cnt = 0;
	IterLinkList<EntInfo> iterInfo(entList->GetInfoList());
	EntInfo *info;

	iterInfo.Reset();
	while(!iterInfo.IsDone())
	{
		info = iterInfo.GetNext();
		if(!info->IsModel())
			AddMenuEntry(pmAdd[info->GetEntClass()->count],
				info->GetClassdesc(), PME_ADDENT, info->GetClassname());
		else if(info->IsModel() && info->GetEntClass()->name != wxT("Null"))
			AddMenuEntry(pmApply[info->GetEntClass()->count],
				info->GetClassdesc(), PME_APPLYENT, info->GetClassname());
	}

	cnt = 0;
	iterClass.Reset();
	while(!iterClass.IsDone())
	{
		entClass = iterClass.GetNext();
		if(entClass->nonmodels)
			addMenu.Append(wxID_ANY, entClass->name, pmAdd[cnt]);
		if(entClass->models && entClass->name != wxT("Null"))
			applyMenu.Append(wxID_ANY, entClass->name, pmApply[cnt]);
		cnt++;
	}
}

void QMainFrame::BuildEntityMenus()
{
	QDraw::OutputText(_("Creating build entity menu... "));

	// Add to the top menu bar.
	wxMenuBar *menuBar = GetMenuBar();
	wxMenu *objectMenu = menuBar->GetMenu(3);
	ASSERT(objectMenu);

	// Get the items from the main menu.
	int addId = XRCID("menuAddEntity");
	int applyId = XRCID("menuApplyEntity");
	wxMenuItem *addEntity = objectMenu->FindItem(addId);
	wxMenuItem *applyEntity = objectMenu->FindItem(applyId);
	ASSERT(addEntity);
	ASSERT(applyEntity);

	// Retrieve the items text.
	wxString addText = addEntity->GetText();
	wxString applyText = applyEntity->GetText();

	// Remove from the main menu
	objectMenu->Delete(addEntity);
	objectMenu->Delete(applyEntity);

	// Add to the main menu.
	wxMenu *addMenu = new wxMenu();
	wxMenu *applyMenu = new wxMenu();
	CreateEntityMenu(*addMenu, *applyMenu);
	objectMenu->Insert(1, addId, addText, addMenu);
	objectMenu->Insert(2, applyId, applyText, applyMenu);

	// Get the items from the popup menu.
	addEntity = pmView->FindItemByPosition(2);
	applyEntity = pmView->FindItemByPosition(3);
	ASSERT(addEntity);
	ASSERT(applyEntity);

	// Remove the items from the popup menu.
	pmView->Remove(addEntity);
	pmView->Remove(applyEntity);

	// Add to the popup menu.
	addMenu = new wxMenu();
	applyMenu = new wxMenu();
	CreateEntityMenu(*addMenu, *applyMenu);
	pmView->Insert(2, wxID_ANY, addText, addMenu);
	pmView->Insert(3, wxID_ANY, applyText, applyMenu);

	QDraw::OutputText(_("OK.\n"));
}

// Child views.
wxPanel *QMainFrame::GetActiveChild()
{
	int selection = documentViews->GetSelection();
	if(selection < 0 || selection >= documentViews->GetPageCount())
		return NULL;
	return static_cast<wxPanel*> (documentViews->GetPage(selection));
}

void QMainFrame::SetActiveChild(wxPanel *panel)
{
	int index = documentViews->GetPageIndex(panel);
	if(index < 0 || index >= documentViews->GetPageCount())
		documentViews->SetSelection(index);
}
	
// Status Bar.
void QMainFrame::UpdateStatusBar(const wxString &text, int id)
{
	wxStatusBar *bar = GetStatusBar();
	bar->SetStatusText(text, id);
}

void QMainFrame::UpdateStatusBar()
{
	switch(operationMode)
	{
	case OPM_OBJECT_SELECT:
	case OPM_OBJECT_MOVE:
	case OPM_OBJECT_ROTATE:
	case OPM_OBJECT_SCALE:
		UpdateStatusBar(_("Hold Ctrl or Shift key for multi-object selection"));
		break;
	case OPM_MODIFY_BRUSH:
	case OPM_FACE_MOVE:
	case OPM_EDGE_MOVE:
	case OPM_VERTEX_MOVE:
		UpdateStatusBar(wxT(""));
		break;
	case OPM_MIRROR:
	case OPM_PLANE_CLIP:
		UpdateStatusBar(_("Right click for operations menu"));
		break;
	case OPM_EYE_MOVE:
		UpdateStatusBar(_("Hold the Shift key to rotate the 3D view"));
		break;
	case OPM_EYE_ROTATE:
		UpdateStatusBar(_("Hold the Ctrl key to move the view(s)"));
		break;
	case OPM_EYE_ZOOM:
		UpdateStatusBar(_("Press the keys + and - anytime for zoom shortcuts"));
		break;
	case OPM_FLY_THROUGH:
		UpdateStatusBar(_("Left mouse click to exit fly through mode"));
		break;
	}

}

// Events
void QMainFrame::OnExit(wxCommandEvent& WXUNUSED(event))
{
    Close();
}

void QMainFrame::OnAbout(wxCommandEvent &event)
{
}

void QMainFrame::OnLeftNotebook(wxAuiNotebookEvent &event)
{
	event.StopPropagation();
}

void QMainFrame::OnDocumentPageChanged(wxAuiNotebookEvent &event)
{
	// Get the new selected page.
	int selectedId = event.GetSelection();
	if(selectedId < 0)
	{
		// No page selected, set the null document.
		SetDeskTopDocument(NULL);
		return;
	}

	// Get the selected page panel.
	wxWindow *selectedPage = documentViews->GetPage(selectedId);
	ASSERT(selectedPage);

	// Cast into a child frame.
	QChildFrame *childFrame = static_cast<QChildFrame*> (selectedPage);
	QooleDoc *newDoc = childFrame->GetDocument();

	// Set the desktop document.
	if(desktopDocument != newDoc && newDoc != NULL)
		SetDeskTopDocument(newDoc);
}

void QMainFrame::OnDocumentPageClosed(wxAuiNotebookEvent &event)
{
	// Get the current child.
	wxPanel *child = GetActiveChild();
	ASSERT(child);
	
	// Notify the child.
	wxCloseEvent closeEvent(wxEVT_CLOSE_WINDOW);
	child->ProcessEvent(closeEvent);
	
	// Add the oppportunity to stop the close.
	if(closeEvent.GetVeto())
		event.Veto();
}

bool QMainFrame::ProcessEvent(wxEvent& event)
{
	if(!docManager || !docManager->ProcessEvent(event))
		return wxFrame::ProcessEvent(event);
	else
		return true;
}

void QMainFrame::OnCloseWindow(wxCloseEvent& event)
{
	if (docManager->Clear(!event.CanVeto()))
	{
		this->Destroy();
	}
	else
		event.Veto();
}

// File menu - export map
void QMainFrame::OnFileExportMap(wxCommandEvent &event)
{
	wxFileDialog dialog(this, _("Export map"), wxT(""), wxT(""), wxT(""), wxSAVE|wxOVERWRITE_PROMPT);
	wxString wildcard;
	wxString mapExt = desktopDocument->GetGame()->GetMapExt();
	wildcard.Printf(_("Map Files (*.%s)|*.%s"), mapExt.c_str(), mapExt.c_str());
	dialog.SetWildcard(wildcard);
	wxFileName docName = desktopDocument->GetFilename();
	docName.SetExt(wxT("map"));
	dialog.SetDirectory(docName.GetFullPath());
	dialog.SetFilename(docName.GetFullName());

	if(dialog.ShowModal() != wxID_OK)
		return;

	desktopDocument->ExportMapFile(dialog.GetFilename());
}

void QMainFrame::OnFileExportMapUI(wxUpdateUIEvent &event)
{
	event.Enable(desktopDocument != NULL);
}

// File menu - compile map
void QMainFrame::OnFileCompileMap(wxCommandEvent &event)
{
	QCompile dialog(this);
	if(dialog.ShowModal() != wxID_OK)
		return;

	QCompile::ExecuteCompiles();
}

void QMainFrame::OnFileCompileMapUI(wxUpdateUIEvent &event)
{
	event.Enable(desktopDocument != NULL);
}

// File menu - stop compile
void QMainFrame::OnFileStopCompile(wxCommandEvent &event)
{
	QCompile::StopCompiles();
}

void QMainFrame::OnFileStopCompileUI(wxUpdateUIEvent &event)
{
	event.Enable(desktopDocument != NULL && processWindow->IsProcessRunning());
}

// Edit menu - Undo and Redo
void QMainFrame::OnEditUndo(wxCommandEvent &event)
{
	ASSERT(opManager.GetNumUndoOps() > 0);

	if (opManager.GetNumUndoOps() > 0)
		opManager.Undo();
}

void QMainFrame::OnEditUndoUI(wxUpdateUIEvent &event)
{
	event.Enable(opManager.GetNumUndoOps() > 0);
}

void QMainFrame::OnEditRedo(wxCommandEvent &event)
{
	ASSERT(opManager.GetNumRedoOps() > 0);

	if (opManager.GetNumRedoOps() > 0)
		opManager.Redo();
}

void QMainFrame::OnEditRedoUI(wxUpdateUIEvent &event)
{
	event.Enable(opManager.GetNumRedoOps() > 0);
}

// Edit menu - Cut
void QMainFrame::OnEditCut(wxCommandEvent &event)
{
	ASSERT(selector != NULL);
	OpEditCut *opCut = new OpEditCut();
	opManager.RegisterOp(*opCut);
}

void QMainFrame::OnEditCutUI(wxUpdateUIEvent &event)
{
	bool flag = (desktopDocument != NULL && selector->GetNumMSelectedObjects() > 0);
	event.Enable(flag);
}

// Edit menu - Copy
void QMainFrame::OnEditCopy(wxCommandEvent &event)
{
	ASSERT(selector != NULL);
	OpEditCopy *opCopy = new OpEditCopy();
	opManager.RegisterOp(*opCopy);
}

void QMainFrame::OnEditCopyUI(wxUpdateUIEvent &event)
{
	bool flag = (desktopDocument != NULL && selector->GetNumMSelectedObjects() > 0);
	event.Enable(flag);
}

// Edit menu - Paste.
void QMainFrame::OnEditPasteUI(wxUpdateUIEvent &event)
{
	// The views has another handler for this, so just disable it here.
	event.Enable(false);
}

void QMainFrame::OnEditHollow(wxCommandEvent &event)
{
	Object *slctObj = NULL;

	ASSERT(selector != NULL);
	ASSERT(selector->GetNumMSelectedObjects() == 1);
	slctObj = (selector->GetMSelectedObjects())[0].GetPtr();

	bool inward = event.GetId() == XRCID("editHollowInward");
	if(promptHollow)
	{
		QHollowPrompt prompt(this, hollowThickness);
		if(prompt.ShowModal() == wxID_OK)
		{
			promptHollow = prompt.prompt == false;
			hollowThickness = prompt.thickness;
		}
		else // wxID_CANCEL.
		{
			return;
		}
	}

	OpObjModification *opHollow =
			OpObjModification::NewHollowOp(*slctObj, inward, hollowThickness);
	if (opHollow)
		opManager.RegisterOp(*opHollow);
}

void QMainFrame::OnEditHollowUI(wxUpdateUIEvent &event)
{
	bool flag = false;

	if (desktopDocument != NULL && selector->GetNumMSelectedObjects() == 1)
	{
		Object *slctObj = (selector->GetMSelectedObjects())[0].GetPtr();
		if (slctObj->HasBrush())
			flag = true;
	}

	event.Enable(flag);
}

void QMainFrame::OnEditDelete(wxCommandEvent &event)
{
	ASSERT(selector != NULL);
	ASSERT(selector->GetNumMSelectedObjects() > 0);
	OpObjsDel *opDel = new OpObjsDel();
	opManager.RegisterOp(*opDel);
}

void QMainFrame::OnEditDeleteUI(wxUpdateUIEvent &event)
{
	bool flag = (selector && selector->GetNumMSelectedObjects() > 0);
	event.Enable(flag);
}

// Edit - CSG subtract
void QMainFrame::OnEditSubtract(wxCommandEvent &event)
{
	OpCSGSubtract *op = OpCSGSubtract::NewSubtractOp();

	if (op != NULL)
		CommitOperation(*op);
}

void QMainFrame::OnEditSubtractUI(wxUpdateUIEvent &event)
{
	int numBrushes = 0;
	int numEnts = 0;
	Object *pObj;

	if (selector)
	{
		IterLinkList<ObjectPtr> iter(selector->GetMSelectedObjects());
		iter.Reset();
		while (!iter.IsDone()) {
			pObj = iter.GetNext()->GetPtr();
			if ((numEnts = pObj->CountItems()) > 0)
				break;
			numBrushes += pObj->CountBrushes();
		}
	}

	bool flag = (numEnts == 0 && numBrushes > 0);
	event.Enable(flag);
}

// Edit - CSG intersect
void QMainFrame::OnEditIntersect(wxCommandEvent &event)
{
	OpCSGIntersect *op = OpCSGIntersect::NewIntersectOp();

	if (op != NULL)
		CommitOperation(*op);
}

void QMainFrame::OnEditIntersectUI(wxUpdateUIEvent &event)
{
	int numBrushes = 0;
	int numEnts = 0;
	Object *pObj;

	if (selector)
	{
		IterLinkList<ObjectPtr> iter(selector->GetMSelectedObjects());
		iter.Reset();
		while (!iter.IsDone()) {
			pObj = iter.GetNext()->GetPtr();
			if ((numEnts = pObj->CountItems()) > 0)
				break;
			numBrushes += pObj->CountBrushes();
		}
	}

	bool flag = (numEnts == 0 && numBrushes > 1);
	event.Enable(flag);
}

void QMainFrame::OnEditPreferences(wxCommandEvent &event)
{
	QDraw::OutputText(_("Loading preferences... "));
	QConfigSheet conf;
	conf.Create(_("Qoole Configs"), this);
	QDraw::OutputText(_("OK.\n"));

	if(conf.ShowModal() == wxID_OK)
		conf.ApplyChanges();
}

void QMainFrame::OnEditTextureManager(wxCommandEvent &event)
{
	QTextureManager texMan(this);
	texMan.ShowModal();
	textureView->OnNewContents();
}

// Mode menu.
void QMainFrame::OnModeObjectMultiTool(wxCommandEvent &event)
{
	SwitchOpMode(OPM_OBJECT_MULTITOOL);
}

void QMainFrame::OnModeObjectMultiToolUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_OBJECT_MULTITOOL);
}

void QMainFrame::OnModeObjectSelect(wxCommandEvent &event)
{
	SwitchOpMode(OPM_OBJECT_SELECT);
}

void QMainFrame::OnModeObjectSelectUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_OBJECT_SELECT);
}

void QMainFrame::OnModeObjectMove(wxCommandEvent &event)
{
	SwitchOpMode(OPM_OBJECT_MOVE);
}

void QMainFrame::OnModeObjectMoveUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_OBJECT_MOVE);
}

void QMainFrame::OnModeObjectRotate(wxCommandEvent &event)
{
	SwitchOpMode(OPM_OBJECT_ROTATE);
}

void QMainFrame::OnModeObjectRotateUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_OBJECT_ROTATE);
}

void QMainFrame::OnModeObjectScale(wxCommandEvent &event)
{
	SwitchOpMode(OPM_OBJECT_SCALE);
}

void QMainFrame::OnModeObjectScaleUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_OBJECT_SCALE);
}

void QMainFrame::OnModeFaceMove(wxCommandEvent &event)
{
	SwitchOpMode(OPM_FACE_MOVE);
}

void QMainFrame::OnModeFaceMoveUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_FACE_MOVE);
}

void QMainFrame::OnModeEdgeMove(wxCommandEvent &event)
{
	SwitchOpMode(OPM_EDGE_MOVE);
}

void QMainFrame::OnModeEdgeMoveUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_EDGE_MOVE);
}

void QMainFrame::OnModeVertexMove(wxCommandEvent &event)
{
	SwitchOpMode(OPM_VERTEX_MOVE);
}

void QMainFrame::OnModeVertexMoveUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_VERTEX_MOVE);
}

void QMainFrame::OnModePlaneClip(wxCommandEvent &event)
{
	SwitchOpMode(OPM_PLANE_CLIP);
}

void QMainFrame::OnModePlaneClipUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_PLANE_CLIP);
}

void QMainFrame::OnModeMirrorFlip(wxCommandEvent &event)
{
	SwitchOpMode(OPM_MIRROR);
}

void QMainFrame::OnModeMirrorFlipUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_MIRROR);
}

void QMainFrame::OnModeEyeMove(wxCommandEvent &event)
{
	SwitchOpMode(OPM_EYE_MOVE);
}

void QMainFrame::OnModeEyeMoveUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_EYE_MOVE);
}

void QMainFrame::OnModeEyeRotate(wxCommandEvent &event)
{
	SwitchOpMode(OPM_EYE_ROTATE);
}

void QMainFrame::OnModeEyeRotateUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_EYE_ROTATE);
}

void QMainFrame::OnModeEyeZoom(wxCommandEvent &event)
{
	SwitchOpMode(OPM_EYE_ZOOM);
}

void QMainFrame::OnModeEyeZoomUI(wxUpdateUIEvent &event)
{
	event.Check(operationMode == OPM_EYE_ZOOM);
}

void QMainFrame::OnModeFlyThroughPreview(wxCommandEvent &event)
{
	SwitchOpMode(OPM_FLY_THROUGH);
}

void QMainFrame::OnModeFlyThroughPreviewUI(wxUpdateUIEvent &event)
{
	event.Enable(false);
	event.Check(operationMode == OPM_FLY_THROUGH);
}

// Edit: Undo and Redo
void QMainFrame::CommitOperation(Operation &op)
{
	opManager.RegisterOp(op);
}


// Object menu - Properties.
void QMainFrame::OnObjectProperties(wxCommandEvent &event)
{
	if (desktopDocument == NULL)
		return;

	if (propSheet)
	{
		propSheet->SetFocus();
		return;
	}

	// Determine which property page to activate.
	QPropSheet::SheetID showPage = QPropSheet::SID_OBJECT;
	if (selector->GetNumMSelectedObjects() == 0)
	{
		// Worldspawn entity.
		showPage = QPropSheet::SID_ENTITY;
	}
	else if (selector->GetNumMSelectedObjects() == 1)
	{
		Object *pSelObj = (selector->GetMSelectedObjects())[0].GetPtr();
		if (pSelObj->HasEntity())
			showPage = QPropSheet::SID_ENTITY;
		else if (pSelObj->IsLeaf())
			showPage = QPropSheet::SID_TEXTURE;
	}

	wxRect *rect = propSheetRect.IsEmpty() ? NULL : &propSheetRect;
	propSheet = new QPropSheet(this, rect);
	propSheet->RegisterDocument(desktopDocument);
	propSheet->Show(true);
	propSheet->SetPage(showPage);
	propSheet->SetFocus();
}

void QMainFrame::OnDestroyPropSheet(const wxRect &rect)
{
	ASSERT(propSheet);
	propSheet = NULL;
	propSheetRect = rect;
}

void QMainFrame::OnDestroyChild(wxPanel *child)
{
	ASSERT(child);
	if(GetActiveChild() == child)
		SetActiveChild(NULL);
		
	// Remove the page.
	int index = documentViews->GetPageIndex(child);
	if(index >= 0 && index < documentViews->GetPageCount())
		documentViews->RemovePage(index);
}

// Object menu - grouping/ungrouping
void QMainFrame::OnObjectGroup(wxCommandEvent &event)
{
	// Sanity.
	ASSERT(selector->GetNumMSelectedObjects() > 1);

	LinkList<ObjectPtr> selObjs;
	selObjs = selector->GetMSelectedObjects();

	OpGrouping *opGroup = new OpGrouping(selObjs);
	opManager.RegisterOp(*opGroup);

	if (promptGroupName)
	{
		// TODO: Start changing the group name in the tree view.
	}
}

void QMainFrame::OnObjectGroupUI(wxUpdateUIEvent &event)
{
	event.Enable(desktopDocument != NULL && selector != NULL
			&& selector->GetNumMSelectedObjects() > 1);
}

void QMainFrame::OnObjectUngroup(wxCommandEvent &event)
{
	// Sanity.
	ASSERT(selector->GetNumMSelectedObjects() == 1);

	Object *selObj = (selector->GetMSelectedObjects())[0].GetPtr();
	OpGrouping *opGroup = new OpGrouping(selObj);
	opManager.RegisterOp(*opGroup);
}

void QMainFrame::OnObjectUngroupUI(wxUpdateUIEvent &event)
{
	bool group = false;

	if(desktopDocument != NULL && selector != NULL &&
		selector->GetNumMSelectedObjects() == 1)
	{
		Object *pObj = (selector->GetMSelectedObjects())[0].GetPtr();
		if (pObj->GetNumChildren() > 0)
			group = true;
	}

	event.Enable(group);
}

// Object menu - Scope Up/Down.
void QMainFrame::OnObjectScopeUp(wxCommandEvent &event)
{
	ASSERT(editScope->GetParentPtr() != NULL);
	Object *pNewScope = editScope->GetParentPtr();
	LinkList<ObjectPtr> slctObjs;

	slctObjs.AppendNode(*(new ObjectPtr(editScope)));

	OpScopeChange *op = new OpScopeChange(pNewScope, slctObjs);
	CommitOperation(*op);
}

void QMainFrame::OnObjectScopeUpUI(wxUpdateUIEvent &event)
{
	bool flag = false;

	if (desktopDocument != NULL && selector != NULL &&
		editScope != NULL && editScope->GetParentPtr() != NULL)
	{
		flag  = true;
	}

	event.Enable(flag);
}

void QMainFrame::OnObjectScopeDown(wxCommandEvent &event)
{
	ASSERT(selector->GetNumMSelectedObjects() == 1);
	Object *pNewScope = (selector->GetMSelectedObjects())[0].GetPtr();
	LinkList<ObjectPtr> slctObjs;

	OpScopeChange *op = new OpScopeChange(pNewScope, slctObjs);
	CommitOperation(*op);
}

void QMainFrame::OnObjectScopeDownUI(wxUpdateUIEvent &event)
{
	bool flag = false;

	if (desktopDocument != NULL && selector != NULL &&
		selector->GetNumMSelectedObjects() == 1)
	{
		Object *pObj = (selector->GetMSelectedObjects())[0].GetPtr();
		if (pObj->GetNumChildren() > 0)
			flag  = true;
	}

	event.Enable(flag);
}

// Load and save prefab.
void QMainFrame::OnObjectLoadPrefab(wxCommandEvent &event)
{
	wxString filters = _("Qoole Files (*.xql; *.qle; *.map)|*.xql;*.qle;*.map");

	//TODO: Add the prefabs manager.
}

void QMainFrame::OnObjectLoadPrefabUI(wxUpdateUIEvent &event)
{
	event.Enable(desktopDocument != NULL);
}

void QMainFrame::OnObjectSavePrefab(wxCommandEvent &event)
{
}

void QMainFrame::OnObjectSavePrefabUI(wxUpdateUIEvent &event)
{
	if(desktopDocument == NULL)
	{
		event.Enable(false);
		return;
	}

	bool enable = true;

	if(selector->GetNumMSelectedObjects() != 1)
	{
		enable = false;
	}
	else
	{
		Object *object = selector->GetMSelectedObjects()[0].GetPtr();
		if(object->IsItemNode())
			enable = false;
	}

	event.Enable(enable);
}

QMainFrame* GetMainFrame()
{
	wxWindow *w = wxGetApp().GetTopWindow();
	if(w && w->IsKindOf(CLASSINFO(QMainFrame)))
		return static_cast<QMainFrame*>(w);
	return NULL;
}

// QViewsState implementation.
QViewsState::QViewsState(void) {

	// Get the desktop document.
	QooleDoc *doc = GetMainFrame()->GetDeskTopDocument();
	ASSERT(doc);

	// Iterate to each one of the views
	wxList &viewList = doc->GetViews();
	wxList::iterator it = viewList.begin();
	for(; it != viewList.end(); it++)
	{
		wxObject *obj = *it;

		// Ignore views that aren't QView
		if(!obj->IsKindOf(CLASSINFO(QView)))
			continue;

		// Cast the view.
		QView *view = static_cast<QView*> (obj);

		// Read the view state.
		ViewState state;
		state.view = view;
		view->GetViewState(state.position, state.orientation, state.zoom);

		// Store the state.
		viewStates.push_back(state);
	}
}

QViewsState::~QViewsState()
{
}

void QViewsState::RestoreQViewsState(void) {
	bool setLockFocus = false;

	for(size_t i = 0; i < viewStates.size(); i++)
	{
		ViewState &state = viewStates[i];
		QView *view = state.view;

		// Check that the view is valid.
		if(!GetMainFrame()->IsQViewValid(view))
			continue;

		// Restore the view state.
		view->SetViewState(state.position, state.orientation, state.zoom);

		if(!setLockFocus && GetMainFrame()->IsLockedView(view))
		{
			setLockFocus = true;
			GetMainFrame()->SetEditFocusPos(state.position, false);
		}
	}
}
